home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / wsanet8a / wsanet / vbsmtpd / vbsmtpd.bas < prev    next >
Encoding:
BASIC Source File  |  1995-12-22  |  28.3 KB  |  1,015 lines

  1. Option Explicit
  2. '                           VB-SMTPD
  3. ' An SMTP daemon state server written with the WSANET.VBX control.
  4.  
  5. Const VERSION_BANNER = "1.0/VB-SMTPSRV"
  6.  
  7. ' Define our SMTP state structure
  8. Type tSMTPSTATE
  9.     iState As Integer
  10.     ' Command state information
  11.     sHelo As String
  12.     sMailFrom As String
  13.     sRcptTo As String
  14.     iMailType As Integer
  15.     ' Message state information
  16.     iFileHandle As Integer
  17.     sFilename As String
  18. End Type
  19.  
  20. ' A connection can only be in 1 of 3 states:
  21. Const SMTP_CLOSEDSTATE = 0
  22. Const SMTP_COMMANDSTATE = 1
  23. Const SMTP_DATASTATE = 2
  24.  
  25. ' Mark any odd handshakes!
  26. Const MAIL_MAIL = 0
  27. Const MAIL_SEND = 1
  28. Const MAIL_SOML = 2
  29. Const MAIL_SAML = 3
  30. Const MAIL_EHLO = 4
  31. Const MAIL_8BITMIME = 8
  32.  
  33. ' Hold the actual dynamically created controls &
  34. ' the number OF them.
  35. Global giIndex As Integer
  36. Global gSMTPState() As tSMTPSTATE
  37.  
  38. ' These are filled in at the start
  39. Global gLocalHostName As String
  40. Global CRLF As String
  41.  
  42. ' These are used when logging
  43. Global Const LOG_NOTHING = 0
  44. Global Const LOG_ERROR = 1
  45. Global Const LOG_WARNING = 2
  46. Global Const LOG_SEND = 4
  47. Global Const LOG_RECV = 8
  48. Global Const LOG_SUMMARY = 16
  49. Global Const LOG_NORMAL = 32
  50. Global Const LOG_DEBUG = 64
  51.  
  52. Global giLogFlags As Integer
  53. Global giLogSize As Integer
  54.  
  55. Global msMailFile As String
  56.  
  57. 'WinAPI Declarations
  58. Declare Function GetTempFileName Lib "Kernel" (ByVal cDriveLetter As Integer, ByVal lpPrefixString As String, ByVal wUnique As Integer, ByVal lpTempFileName As String) As Integer
  59. Declare Function GetTempDrive Lib "Kernel" (ByVal cDriveLetter As Integer) As Integer
  60.  
  61. Global Const OFN_FILEMUSTEXIST = &H1000&
  62. Global Const OFN_PATHMUSTEXIST = &H800&
  63. Global Const OFN_READONLY = &H1&
  64.  
  65. Private Function Date2HEX (d#) As String
  66. Dim seconds#
  67.  
  68.     ' Stolen from a VB magazine (which I can't remember the name of)
  69.     seconds# = (d# - 1) * 86400
  70.  
  71.     If seconds# > 4294967294# Then
  72.         Date2HEX = ""
  73.     ElseIf seconds# < 2147483647# Then
  74.         Date2HEX = Hex$(seconds# + 1)
  75.     Else
  76.         Date2HEX = Hex$(seconds# - 4294967295#)
  77.     End If
  78.  
  79.  
  80. End Function
  81.  
  82. Sub gSMTPAccept (Socket As Integer, PeerName As String, RemotePort As Integer)
  83. Dim sDateTime As String
  84. Dim Index As Integer
  85. Dim cNetClient As NetClient
  86.  
  87.     On Error GoTo AcceptError
  88.  
  89.     gSMTPLog LOG_DEBUG, "gSMTPAccept(" & Str$(Socket) & ", " & PeerName & "," & Str$(RemotePort) & ")"
  90.  
  91.     gSMTPSocketOpen Index
  92.  
  93.     If Index = 0 Then
  94.         gSMTPLog LOG_ERROR, "gSMTPSocketOpen() Failed!"
  95.         Socket = 0
  96.         Exit Sub
  97.     End If
  98.  
  99.     Main!NetClient(Index).Socket = Socket
  100.     
  101.     ' If for some reason, there is an error, say goodbye!
  102.     If Err <> 0 Then
  103.         gSMTPSend Index, 421, "Service unavailable. Closing transmission channel."
  104.         gSMTPLog LOG_ERROR, "Connection from [" & PeerName & "] refused!"
  105.         gSMTPLog LOG_DEBUG, "Err =" & Str$(Err) & ": " & Error$
  106.         Socket = 0
  107.         Exit Sub
  108.     End If
  109.  
  110.     gSMTPLog LOG_NORMAL, "Connection from:" & Main!NetClient(Index).HostName & "(" & PeerName & ")"
  111.  
  112.     Main!NetClient(Index).LineDelimiter = CRLF
  113.     
  114.     gSMTPState(Index).iState = SMTP_COMMANDSTATE
  115.     gSMTPState(Index).sHelo = ""
  116.     gSMTPState(Index).sMailFrom = ""
  117.     gSMTPState(Index).sRcptTo = ""
  118.  
  119.     'Tue, 14 Dec 93 10:14:13 PST
  120.     sDateTime = Format$(Now, "ddd, d mmm yy hh:nn:ss")
  121.  
  122.     gSMTPSend giIndex, 220, gLocalHostName & " SendMail v1.00/VB-WSMTPD ready at " & sDateTime
  123.  
  124.     Exit Sub
  125.  
  126. AcceptError:
  127.     gSMTPError Erl, Err, Error$
  128.     Resume Next
  129.  
  130. End Sub
  131.  
  132. Sub gSMTPCleanup ()
  133.     
  134.     ' Close all connections
  135.     While giIndex > 0
  136.         gSMTPSend giIndex, 421, "Service shutting down"
  137.         gSMTPSocketClose giIndex
  138.     Wend
  139.  
  140.     Main!Ini.Section = "Configuration"
  141.  
  142.     gSMTPLogPutFlag "LogError", LOG_ERROR
  143.     gSMTPLogPutFlag "LogWarning", LOG_WARNING
  144.     gSMTPLogPutFlag "LogSend", LOG_SEND
  145.     gSMTPLogPutFlag "LogRecv", LOG_RECV
  146.     gSMTPLogPutFlag "LogSummary", LOG_SUMMARY
  147.     gSMTPLogPutFlag "LogNormal", LOG_NORMAL
  148.     gSMTPLogPutFlag "LogDebug", LOG_DEBUG
  149.  
  150. End Sub
  151.  
  152. Sub gSMTPClose (Index As Integer)
  153. Dim cNetClient As NetClient
  154.  
  155.     gSMTPLog LOG_WARNING, "Lost connection to " & Main!NetClient(Index).HostName & "!"
  156.     
  157.     gSMTPSocketClose Index
  158.  
  159. End Sub
  160.  
  161. Function gSMTPDoHelo (Index As Integer, sParameters As String) As String
  162.     
  163.     gSMTPState(Index).sHelo = sParameters
  164.     If sParameters <> "" Then
  165.         If sParameters <> Main!NetClient(Index).HostName Then
  166.             gSMTPDoHelo = "Hello " & Main!NetClient(Index).HostName & ", why do you call yourself " & sParameters & "?"
  167.         Else
  168.             gSMTPDoHelo = "Hello " & sParameters & ", glad to meet you."
  169.         End If
  170.     Else
  171.         gSMTPDoHelo = "Thanks for the help, bub!"
  172.     End If
  173.  
  174. End Function
  175.  
  176. Function gSMTPDoHelp (Index As Integer, sParameters As String) As String
  177. Dim sHelp As String
  178. Dim sTemp As String
  179. Dim sSection As String
  180. Dim iLine As Integer
  181. Dim sLine As String
  182.  
  183.     
  184.     gSMTPLog LOG_WARNING, "SMTP session requested HELP on '" & sParameters & "'"
  185.     
  186.     If sParameters = "" Then sParameters = "HELP"
  187.     
  188.     ' Try the [Help] section mappings first
  189.     Main!Ini.Section = "Help"
  190.     Main!Ini.Entry = sParameters
  191.  
  192.     If Main!Ini.Value = "" Then
  193.         gSMTPLog LOG_DEBUG, "Requested [HELP] has no mapping '" & sParameters & "= {}'"
  194.         sSection = sParameters
  195.     Else
  196.         sSection = Main!Ini.Value
  197.     End If
  198.  
  199.     ' Set the section of the Help topic to use
  200.     Main!Ini.Section = sSection
  201.  
  202.     ' Loop until no more entries are found
  203.     iLine = 1
  204.     sHelp = ""
  205.     Do
  206.         sLine = Str$(iLine)
  207.         sLine = Right$(sLine, Len(sLine) - 1)
  208.         Main!Ini.Entry = "Line" & sLine
  209.         sLine = Main!Ini.Value
  210.         If sLine <> "" Then
  211.             sHelp = sHelp & sLine & "+"
  212.         End If
  213.         iLine = iLine + 1
  214.     Loop While sLine <> ""
  215.  
  216.     If sHelp <> "" Then
  217.         gSMTPDoHelp = Left$(sHelp, Len(sHelp) - 1)
  218.     Else
  219.         gSMTPDoHelp = sHelp
  220.     End If
  221.  
  222. End Function
  223.  
  224. Sub gSMTPDoMail (Index As Integer, sParameters As String)
  225. Dim iPlace As Integer
  226. Dim sTemp As String
  227. Dim sExtended As String
  228.     
  229.     sTemp = UCase$(sParameters)
  230.  
  231.     If Left$(sTemp, 5) = "FROM:" Then
  232.         If gSMTPState(Index).sMailFrom = "" Then
  233.             sTemp = Right$(sParameters, Len(sParameters) - 5)
  234.             ' If E/SMTP is in action
  235.             If gSMTPState(Index).iMailType And MAIL_EHLO Then
  236.                 ' Check for more after the address
  237.                 iPlace = InStr(sTemp, "> ")
  238.                 If iPlace > 0 Then
  239.                     sExtended = Trim$(Right$(sTemp, Len(sTemp) - iPlace - 1))
  240.                     While sExtended <> ""
  241.                         gSMTPDoMailExt Index, sExtended
  242.                     Wend
  243.                 End If
  244.             End If
  245.             
  246.             gSMTPState(Index).sMailFrom = sTemp
  247.             gSMTPSend Index, 250, sParameters & " Sender Ok."
  248.         Else
  249.             gSMTPSend Index, 503, sTemp & "Sender already specified!"
  250.         End If
  251.     Else
  252.         gSMTPSend Index, 550, "MAIL '" & sParameters & "' Format bad."
  253.     End If
  254.  
  255. End Sub
  256.  
  257. Sub gSMTPDoMailExt (Index As Integer, sExtended As String)
  258. Dim iPlace As String
  259. Dim sKeyWord As String
  260. Dim sTemp As String
  261.  
  262.  
  263.     ' Don't do any E/SMTP commands yet
  264.  
  265.     sExtended = ""
  266.     Exit Sub
  267.     
  268.     ' Put it into parseable form
  269. '    sExtended = UCase$(sExtended)
  270.     
  271.     ' Recurse down until the end is found
  272. '    iPlace = InStr(sExtended, ",")
  273. '    If iPlace > 0 Then
  274. '        Do While iPlace > 0
  275. '            sTemp = Right$(sExtended, Len(sExtended) - iPlace - 1)
  276. '            gSMTPDoMailExt Index, sTemp
  277. '            sExtended = Left$(sExtended, iPlace - 1)
  278. '            iPlace = InStr(sExtended, ",")
  279. '        Loop
  280. '    End If
  281.  
  282.     ' Rip out the extended portion
  283. '    iPlace = InStr(sTemp, "=")
  284. '    If iPlace > 0 Then
  285.         ' Now check the left side
  286. '        sTemp = Trim$(Left$(sExtended, iPlace - 1))
  287. '        Select Case sTemp
  288. '            Case "BODY"
  289. '                sTemp = Trim$(Right$(sExtended, Len(sExtended) - iPlace - 1))
  290. '                If sTemp = "8BITMIME" Then
  291. '                    gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_8BITMIME
  292. '                End If
  293. '             Case "SIZE"
  294.                 ' Not Implemented yet
  295. '            Case Else
  296. '        End Select
  297.  
  298. '    sExtended = ""
  299.  
  300. End Sub
  301.  
  302. Sub gSMTPDoRcpt (Index As Integer, sParameters As String)
  303. Dim iPlace As Integer
  304. Dim sTemp As String
  305.     
  306.     sTemp = UCase$(sParameters)
  307.  
  308.     If Left$(sTemp, 3) = "TO:" Then
  309.         sTemp = Right$(sParameters, Len(sParameters) - 3)
  310.         If gSMTPState(Index).sRcptTo = "" Then
  311.             gSMTPState(Index).sRcptTo = sTemp
  312.         Else
  313.             gSMTPState(Index).sRcptTo = gSMTPState(Index).sRcptTo & "," & sTemp
  314.         End If
  315.         gSMTPSend Index, 250, sTemp & "Recipient Ok."
  316.     Else
  317.         gSMTPSend Index, 501, "Syntax error in parameters: 'RCPT " & sParameters & "'"
  318.     End If
  319.  
  320. End Sub
  321.  
  322. Sub gSMTPError (iLine As Integer, iNumber As Integer, sString As String)
  323. ' This subroutine logs all SMTP and VB errors
  324.  
  325.     Select Case iLine
  326.         Case 0:
  327.             If iNumber = 0 Then
  328.                 gSMTPLog LOG_WARNING, sString
  329.             Else
  330.                 gSMTPLog LOG_WARNING, "VB Warning" + Str$(iNumber) + ": " & sString
  331.             End If
  332.         
  333.         Case -1:
  334.             gSMTPLog LOG_WARNING, "WSA Error:" & Str$(iNumber) & " " & sString
  335.  
  336.         Case Else:
  337.             gSMTPLog LOG_ERROR, "VB Error:" & Str$(iNumber) & " on line" & Str$(iLine) & ": " & sString
  338.     
  339.     End Select
  340.  
  341. End Sub
  342.  
  343. Sub gSMTPFileClose (Index As Integer)
  344. Dim iFileHandle As Integer
  345. Dim sTemp As String
  346. Dim i%
  347. Dim sReader As String
  348. Dim hIn As Integer
  349. Dim hOut As Integer
  350. Dim lSize As Long
  351. Dim iRead As Integer
  352.  
  353.     On Error GoTo FileCloseError
  354.  
  355.     iFileHandle = gSMTPState(Index).iFileHandle
  356.     Close #iFileHandle
  357.     gSMTPState(Index).iFileHandle = 0
  358.     
  359.     If msMailFile <> "" Then
  360.         hIn = FreeFile
  361.         Open gSMTPState(Index).sFilename For Input As #hIn
  362.         hOut = FreeFile
  363.         Open msMailFile For Append As #hOut
  364.         lSize = LOF(hIn)
  365.         Do While Not EOF(hIn)
  366.             If lSize = 0 Then Exit Do
  367.             If lSize > 16384 Then
  368.                 iRead = 16384
  369.             Else
  370.                 iRead = lSize
  371.             End If
  372.             
  373.             sTemp = Input$(iRead, hIn)
  374.             Print #hOut, sTemp;
  375.             lSize = lSize - iRead
  376.         Loop
  377.         Close #hIn
  378.         Close #hOut
  379.  
  380.     End If
  381.     
  382.     Main!Ini.Section = "Configuration"
  383.     Main!Ini.Entry = "Reader"
  384.     sReader = Main!Ini.Value
  385.     If sReader <> "" Then
  386.         Main!Ini.Entry = "UniqueReader"
  387.         sTemp = Main!Ini.Value
  388.         If sTemp = "1" Or Left$(sTemp, 1) = "T" Or Right$(sTemp, 1) = "N" Then
  389.             If msMailFile = "" Then
  390.                 sTemp = sReader
  391.             Else
  392.                 sTemp = sReader & " " & msMailFile
  393.             End If
  394.             i% = Shell(sTemp)
  395.         Else
  396.             sTemp = sReader & " " & gSMTPState(Index).sFilename
  397.             i% = Shell(sTemp, 1)
  398.         End If
  399.     End If
  400.  
  401. FileCloseError:
  402.     Resume Next
  403. End Sub
  404.  
  405. Sub gSMTPFileOpen (Index As Integer)
  406. Dim hFile As Integer
  407. Dim sIdentifier As String
  408. Dim sHostName As String
  409. Dim sHostAddr As String
  410. Dim sMsgId As String
  411. Dim sDate As String
  412. Dim sTempFile As String
  413. Dim iTemp%
  414. Dim sHelo As String
  415. Dim sRcptTo As String
  416. Dim sMailFrom As String
  417. Dim sMsgLine As String
  418. Dim sTemp As String
  419.  
  420.     On Error GoTo FileOpenError
  421.  
  422.     hFile = FreeFile
  423.     If Err Then Exit Sub
  424.     
  425.     ' Wed, 15 Dec 93 21:48:00
  426.     sDate = Format$(Now, "ddd, d mmm yy hh:nn:ss")
  427.     
  428.     ' Create a temporary file based on the date AND time
  429.     sMsgId = Date2HEX(Now)
  430.     iTemp% = GetTempDrive(iTemp%)
  431.     sTempFile = String$(144, 0)
  432.     iTemp% = GetTempFileName(iTemp%, sMsgId, 0, sTempFile)
  433.     iTemp% = InStr(sTempFile, Chr$(0))
  434.     If iTemp% <> 0 Then
  435.         sTempFile = Left$(sTempFile, iTemp% - 1)
  436.     End If
  437.  
  438.     ' Store the filehandle and filename for later
  439.     gSMTPState(Index).iFileHandle = hFile
  440.     gSMTPState(Index).sFilename = sTempFile
  441.     
  442.     ' Who are we talking to?
  443.     sHostName = Main!NetClient(Index).HostName
  444.     sHostAddr = Main!NetClient(Index).HostAddr
  445.  
  446.     ' Get the handshake info back to build the header
  447.     sHelo = gSMTPState(Index).sHelo
  448.     sRcptTo = gSMTPState(Index).sRcptTo
  449.     sMailFrom = gSMTPState(Index).sMailFrom
  450.  
  451.     ' Open the tempory file for output
  452.     Open sTempFile For Output As #hFile
  453.     
  454.     ' Make up a version string depending on the handshaking
  455.     If gSMTPState(Index).iMailType And MAIL_8BITMIME Then
  456.         sIdentifier = "(" & VERSION_BANNER & "/8BITMIME)"
  457.     Else
  458.         sIdentifier = "(" & VERSION_BANNER & ")"
  459.     End If
  460.  
  461.     Print #hFile,
  462.     
  463.     sTemp = Mid$(sMailFrom, 2, Len(sMailFrom) - 2)
  464.     sMsgLine = "From " & sTemp & " " & sDate
  465.     gSMTPLog LOG_SUMMARY, sMsgLine
  466.     Print #hFile, sMsgLine
  467.     
  468.     sMsgLine = "X-Envelope-To: " & sRcptTo
  469.     gSMTPLog LOG_SUMMARY, sMsgLine
  470.     Print #hFile, sMsgLine
  471.     
  472.     sMsgLine = "Return-Path: " & sMailFrom
  473.     gSMTPLog LOG_SUMMARY, sMsgLine
  474.     Print #hFile, sMsgLine
  475.     
  476.     sMsgLine = "Received: from " & sHostName & " [" & sHostAddr & "] by " & gLocalHostName
  477.     gSMTPLog LOG_SUMMARY, sMsgLine
  478.     Print #hFile, sMsgLine
  479.     
  480.     sMsgLine = Chr$(9) & "id " & sMsgId & " " & sDate
  481.     gSMTPLog LOG_SUMMARY, sMsgLine
  482.     Print #hFile, sMsgLine
  483.     
  484.     Exit Sub
  485.  
  486. FileOpenError:
  487.     gSMTPError Erl, Err, Error$
  488.     Resume Next
  489.  
  490. End Sub
  491.  
  492. Sub gSMTPFileWrite (Index As Integer, sString As String)
  493. Dim iFileHandle As Integer
  494.  
  495.     iFileHandle = gSMTPState(Index).iFileHandle
  496.     Print #iFileHandle, sString
  497.  
  498. End Sub
  499.  
  500. Sub gSMTPLog (iLevel As Integer, sString As String)
  501. ' This routine logs incoming conversations
  502.  
  503.     Debug.Print "Level" & Str$(iLevel) & ": " & sString
  504.  
  505.     Select Case iLevel
  506.         Case LOG_NOTHING:
  507.         
  508.         Case LOG_ERROR:
  509.             If giLogFlags And LOG_ERROR Then
  510.                 gSMTPLogWrite "Error: " & sString
  511.             End If
  512.         
  513.         Case LOG_WARNING:
  514.             If giLogFlags And LOG_WARNING Then
  515.                 gSMTPLogWrite "Warning: " & sString
  516.             End If
  517.         
  518.         Case LOG_SEND:
  519.             If giLogFlags And LOG_SEND Then
  520.                 gSMTPLogWrite "Send: " & sString
  521.             End If
  522.         
  523.         Case LOG_RECV:
  524.             If giLogFlags And LOG_RECV Then
  525.                 gSMTPLogWrite "Recv: " & sString
  526.             End If
  527.         
  528.         Case LOG_SUMMARY:
  529.             If giLogFlags And LOG_SUMMARY Then
  530.                 gSMTPLogWrite "Summary: " & sString
  531.             End If
  532.  
  533.         Case LOG_NORMAL:
  534.             gSMTPLogWrite sString
  535.  
  536.         Case LOG_DEBUG:
  537.             If giLogFlags And LOG_DEBUG Then
  538.                 gSMTPLogWrite "Debug: " & sString
  539.             End If
  540.     End Select
  541.  
  542. End Sub
  543.  
  544. Sub gSMTPLogChkFlag (sEntry As String, iValue As Integer)
  545. Dim sTemp As String
  546.  
  547.     Main!Ini.Entry = sEntry
  548.     sTemp = Main!Ini.Value
  549.     If sTemp = "" Then
  550.         gSMTPLogSetFlags iValue
  551.         Exit Sub
  552.     End If
  553.     
  554.     If (Left$(sTemp, 1) = "1") Or (Left$(sTemp, 1) = "T") Then
  555.         gSMTPLogSetFlags iValue
  556.     End If
  557. End Sub
  558.  
  559. Function gSMTPLogGetFlags () As Integer
  560.     
  561.     gSMTPLogGetFlags = giLogFlags
  562.  
  563. End Function
  564.  
  565. Sub gSMTPLogPutFlag (sEntry As String, iValue As Integer)
  566.  
  567.     Main!Ini.Entry = sEntry
  568.     
  569.     If giLogFlags And iValue Then
  570.         Main!Ini.Value = "True"
  571.     Else
  572.         Main!Ini.Value = "False"
  573.     End If
  574.  
  575. End Sub
  576.  
  577. Sub gSMTPLogSetFlags (iLevel As Integer)
  578.  
  579.     If iLevel = LOG_NOTHING Then
  580.         giLogFlags = LOG_NOTHING
  581.     Else
  582.         giLogFlags = giLogFlags Or iLevel
  583.     End If
  584.  
  585. End Sub
  586.  
  587. Sub gSMTPLogWrite (sString As String)
  588. Dim iTemp As Integer
  589.  
  590.     On Error GoTo LogWriteError
  591.  
  592.     Main!ListHosts.AddItem sString
  593.     
  594.     ' Auto scroll the window
  595.     iTemp = Main!ListHosts.ListCount
  596.     If iTemp = (giLogSize + 2) Then
  597.         Main!ListHosts.RemoveItem 0
  598.         Main!ListHosts.ListIndex = giLogSize
  599.     Else
  600.         If iTemp > 0 Then Main!ListHosts.ListIndex = iTemp - 1
  601.     End If
  602.  
  603.     Exit Sub
  604.  
  605. LogWriteError:
  606.     Main!ListHosts.Clear
  607.     gSMTPLog LOG_WARNING, "Window full, listbox cleared"
  608.     Resume Next
  609.  
  610. End Sub
  611.  
  612. Sub gSMTPParse (Index As Integer, sCommand As String, sParameters As String)
  613. Dim sHelo As String
  614.     
  615.     Select Case sCommand
  616.         Case "EHLO":
  617.             ' Extended SMTP (E/SMTP) identifier
  618.             sHelo = gSMTPDoHelo(Index, sParameters)
  619.             sHelo = sHelo & " Extended SMTP:+HELP+VERB+ONEX+MULT+EXPN+TICK+XWIN8"
  620.             gSMTPSend Index, 250, sHelo
  621.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_EHLO
  622.  
  623.         Case "HELO":
  624.             ' The remote machine says HELlO
  625.             sHelo = gSMTPDoHelo(Index, sParameters)
  626.             gSMTPSend Index, 250, sHelo
  627.  
  628.         Case "SEND":
  629.             ' Send to screen
  630.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_SEND
  631.             gSMTPDoMail Index, sParameters
  632.  
  633.         Case "SAML":
  634.             ' Send to screen and mailbox
  635.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_SAML
  636.             gSMTPDoMail Index, sParameters
  637.         
  638.         Case "SOML":
  639.             ' Send to screen or mailbox
  640.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_SOML
  641.             gSMTPDoMail Index, sParameters
  642.         
  643.         Case "MAIL":
  644.             ' Send to mailbox
  645.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType Or MAIL_MAIL
  646.             gSMTPDoMail Index, sParameters
  647.         
  648.         Case "RCPT":
  649.             ' Identify recipient
  650.             gSMTPDoRcpt Index, sParameters
  651.  
  652.         Case "DATA":
  653.             ' Start receiving the message
  654.             If gSMTPState(Index).sMailFrom = "" Then
  655.                 gSMTPSend Index, 550, "You haven't told me who sent the message!"
  656.                 Exit Sub
  657.             End If
  658.  
  659.             If gSMTPState(Index).sRcptTo = "" Then
  660.                 gSMTPSend Index, 550, "Who am I supposed to give the message to!?!?"
  661.                 Exit Sub
  662.             End If
  663.  
  664.             If gSMTPState(Index).sHelo = "" Then
  665.                 gSMTPState(Index).sHelo = "UNIDENTIFIED"
  666.             End If
  667.  
  668.             gSMTPFileOpen Index
  669.             If Err <> 0 Then
  670.                 gSMTPSend Index, 550, "Local Error: " + Error$
  671.             Else
  672.                 gSMTPSend Index, 354, "Enter message, terminate with '.' alone on a line."
  673.                 gSMTPState(Index).iState = SMTP_DATASTATE
  674.             End If
  675.  
  676.         Case "QUIT":
  677.             gSMTPSend Index, 221, gLocalHostName & " closing connection. Goodbye!"
  678.             Main!NetClient(Index).Connect = False
  679.             gSMTPSocketClose Index
  680.  
  681.         Case "RSET":
  682.             ' Reset the "state" of the connection
  683.             gSMTPState(Index).iState = SMTP_COMMANDSTATE
  684.             gSMTPState(Index).sHelo = ""
  685.             gSMTPState(Index).sMailFrom = ""
  686.             gSMTPState(Index).sRcptTo = ""
  687.             gSMTPState(Index).iMailType = gSMTPState(Index).iMailType And MAIL_EHLO
  688.             gSMTPSend Index, 250, "Reset state."
  689.  
  690.         Case "VERB":
  691.             gSMTPSend Index, 550, "I know that command, but do not implement it."
  692.         
  693.         Case "ONEX":
  694.             gSMTPSend Index, 250, "Yes, I can do one messages at a time."
  695.         
  696.         Case "MULT":
  697.             gSMTPSend Index, 250, "Yes, I can do multiple messages."
  698.         
  699.         Case "VRFY":
  700.             gSMTPSend Index, 250, "They're ok, I guess."
  701.  
  702.         Case "EXPN":
  703.             gSMTPSend Index, 250, "They're ok, I guess."
  704.         
  705.         Case "XWIN3":
  706.             gSMTPSend Index, 250, "Wow! Another Windows addict!"
  707.         
  708.         Case "TURN":
  709.             gSMTPSend Index, 502, "I can't act as a client, yet..."
  710.         
  711.         Case "NOOP":
  712.             gSMTPSend Index, 250, "Ok. *twiddling thumbs*"
  713.  
  714.         Case "TICK":
  715.             gSMTPSend Index, 250, "Ok."
  716.  
  717.         Case "SHOWQ":
  718.             gSMTPSend Index, 550, "Hacker alert! Lame attack!"
  719.         
  720.         Case "DEBUG":
  721.             gSMTPSend Index, 550, "Who you calling buggy?"
  722.  
  723.         Case "HELP":
  724.             Dim sHelp As String
  725.  
  726.             sHelp = gSMTPDoHelp(Index, sParameters)
  727.             If sHelp = "" Then sHelp = "Sorry, I have no help on " & sParameters & "."
  728.             sHelp = sHelp & "+End of HELP."
  729.             gSMTPSend Index, 214, sHelp
  730.  
  731.         Case Else:
  732.             gSMTPLog LOG_WARNING, "Can't parse this: '" & sCommand & " " & sParameters & "'"
  733.             gSMTPSend Index, 500, "Syntax error, command unrecognized."
  734.     
  735.     End Select
  736. End Sub
  737.  
  738. Sub gSMTPReceive (Index As Integer)
  739. Dim sTemp As String
  740. Dim sCommand As String
  741. Dim sParameters As String
  742. Dim iPlace As Integer
  743.  
  744.   'gSMTPLog LOG_DEBUG, "gSMTPReceive(" & Str$(Index) & " )"
  745.  
  746.   On Error GoTo ReceiveError
  747.   
  748.   If Main!NetClient(Index).RecvCount = 0 Then Exit Sub
  749.     
  750.   Do
  751.     If gSMTPState(Index).iState = SMTP_COMMANDSTATE Then
  752.         Do
  753.             sTemp = Main!NetClient(Index)
  754.             If sTemp = "" Then
  755.                 If Main!NetClient(Index).RecvCount = 0 Then
  756.                     gSMTPSend Index, 500, "Try HELP if you're lost."
  757.                 End If
  758.                 Exit Sub
  759.             End If
  760.             gSMTPLog LOG_RECV, sTemp
  761.             iPlace = InStr(sTemp, " ")
  762.             If iPlace <> 0 Then
  763.                 sCommand = Left$(sTemp, iPlace - 1)
  764.                 sParameters = Right$(sTemp, Len(sTemp) - iPlace)
  765.             Else
  766.                 sCommand = sTemp
  767.                 sParameters = ""
  768.             End If
  769.             sCommand = UCase$(sCommand)
  770.             gSMTPParse Index, sCommand, sParameters
  771.             If Not gSMTPSocketIsValid(Index) Then Exit Do
  772.             If gSMTPState(Index).iState <> SMTP_COMMANDSTATE Then Exit Do
  773.             sTemp = ""
  774.         Loop While (Main!NetClient(Index).RecvCount > 0)
  775.     Else
  776.         ' Message body!
  777.         Do
  778.             sTemp = Main!NetClient(Index)
  779. '            If sTemp = "" Then Exit Do
  780.             gSMTPLog LOG_RECV, sTemp
  781.             Select Case sTemp
  782.                 Case ".."
  783.                     gSMTPFileWrite Index, "."
  784.  
  785.                 Case "."
  786.                     gSMTPState(Index).iState = SMTP_COMMANDSTATE
  787.                     gSMTPFileClose Index
  788.                     If Err <> 0 Then
  789.                         gSMTPSend Index, 550, "Error: " + Error$
  790.                     Else
  791.                         gSMTPSend Index, 250, "Ok."
  792.                     End If
  793.                     Exit Do
  794.  
  795.                 Case Else
  796.                     gSMTPFileWrite Index, sTemp
  797.             End Select
  798.             If Not gSMTPSocketIsValid(Index) Then Exit Do
  799.         Loop While (Main!NetClient(Index).RecvCount > 0)
  800.     End If
  801.   
  802.     DoEvents
  803.     If Not gSMTPSocketIsValid(Index) Then Exit Do
  804.   Loop While (Main!NetClient(Index).RecvCount > 0)
  805.  
  806.   Exit Sub
  807.  
  808. ReceiveError:
  809.     gSMTPError Erl, Err, Error$
  810.     Resume Next
  811.  
  812. End Sub
  813.  
  814. Sub gSMTPSend (Index As Integer, iCode As Integer, sString As String)
  815. Dim sCode As String
  816. Dim sLine As String
  817. Dim iPlace As Integer
  818. Dim iReplace As Integer
  819.  
  820.     On Error GoTo SendError
  821.     
  822.     ' This is the code that sends SMTP response lines
  823.     sCode = Str$(iCode)
  824.     sCode = Right$(sCode, Len(sCode) - 1)
  825.     
  826.     '
  827.     ' Replace "_" at the beginning of any line to be sent
  828.     If Left$(sString, 1) = "_" Then
  829.         iReplace = 1
  830.         Do While Right$(Left$(sString, iReplace), 1) = "_"
  831.             iReplace = iReplace + 1
  832.         Loop
  833.         Mid$(sString, 1) = String$(iReplace, " ")
  834.     End If
  835.  
  836.     iPlace = InStr(sString, "+")
  837.  
  838.     ' Multi-line responses
  839.     Do While iPlace <> 0
  840.         sLine = Left$(sString, iPlace - 1)
  841.         ' Replace "_" at the beginning of any line to be sent
  842.         If Left$(sLine, 1) = "_" Then
  843.             iReplace = 1
  844.             Do While Right$(Left$(sLine, iReplace), 1) = "_"
  845.                 iReplace = iReplace + 1
  846.             Loop
  847.             Mid$(sLine, 1) = String$(iReplace - 1, " ")
  848.         End If
  849.         sLine = sCode & "-" & sLine
  850.         
  851.         Main!NetClient(Index) = sLine & CRLF
  852.         gSMTPLog LOG_SEND, sLine
  853.         sString = Right$(sString, Len(sString) - iPlace)
  854.         iPlace = InStr(sString, "+")
  855.     Loop
  856.     sLine = sCode & " " & sString
  857.     Main!NetClient(Index) = sLine & CRLF
  858.     gSMTPLog LOG_SEND, sLine
  859.     
  860.     ' Be Nice
  861.     DoEvents
  862.     Exit Sub
  863.  
  864. SendError:
  865.     gSMTPError Erl, Err, Error$
  866.     Resume Next
  867.  
  868. End Sub
  869.  
  870. Sub gSMTPSocketClose (Index As Integer)
  871. ' This routine is called to close a dynamic connection
  872.  
  873. ' *DIRTY* But it has to be done to close a communications
  874. ' channel. Replace this with your form and control array.
  875.  
  876.     Unload Main!NetClient(Index)
  877.  
  878.     ' If this is the "last" control in the array, then
  879.     ' decrement the "number" count, otherwise just mark
  880.     ' it as available
  881.     If Index = giIndex Then
  882.         gSMTPState(giIndex).iState = SMTP_CLOSEDSTATE
  883.  
  884.         While giIndex > 0
  885.             If gSMTPState(giIndex).iState = SMTP_CLOSEDSTATE Then
  886.                 giIndex = giIndex - 1
  887.                 ReDim Preserve gSMTPState(giIndex) As tSMTPSTATE
  888.             End If
  889.         Wend
  890.  
  891.     Else
  892.      
  893.         gSMTPState(Index).iState = SMTP_CLOSEDSTATE
  894.                         
  895.     End If
  896.  
  897. End Sub
  898.  
  899. Function gSMTPSocketIsValid (Index As Integer) As Integer
  900.  
  901.     If Index > giIndex Then
  902.         gSMTPSocketIsValid = False
  903.         Exit Function
  904.     End If
  905.  
  906.     If gSMTPState(Index).iState = SMTP_CLOSEDSTATE Then
  907.         gSMTPSocketIsValid = False
  908.         Exit Function
  909.     End If
  910.  
  911.     gSMTPSocketIsValid = True
  912. End Function
  913.  
  914. Sub gSMTPSocketOpen (Index As Integer)
  915. Dim iIndex As Integer
  916. ' *DIRTY* But it has to be done to allocate a communications
  917. ' channel. Replace these with your form and control array.
  918.  
  919.     'Make sure the element exists!
  920.     ReDim Preserve gSMTPState(giIndex) As tSMTPSTATE
  921.     
  922.     If giIndex > 0 Then
  923.         For iIndex = 1 To giIndex
  924.             If gSMTPState(iIndex).iState = SMTP_CLOSEDSTATE Then
  925.                 gSMTPState(iIndex).iState = SMTP_COMMANDSTATE
  926.                 Load Main!NetClient(iIndex)
  927.                 Index = iIndex
  928.                 Exit Sub
  929.             End If
  930.         Next
  931.     End If
  932.  
  933.     giIndex = giIndex + 1
  934.  
  935.     Load Main!NetClient(giIndex)
  936.  
  937.     ReDim Preserve gSMTPState(giIndex) As tSMTPSTATE
  938.                 
  939.     gSMTPState(giIndex).iState = SMTP_COMMANDSTATE
  940.     Index = giIndex
  941.  
  942. End Sub
  943.  
  944. Sub gSMTPStartup (cNetServer As NetServer)
  945. Dim sFlags As String
  946.  
  947.     On Error GoTo InitError
  948.     
  949.     Main!Ini.Section = "Configuration"
  950.     gSMTPLogSetFlags LOG_NOTHING
  951.  
  952.     gSMTPLogChkFlag "LogError", LOG_ERROR
  953.     gSMTPLogChkFlag "LogWarning", LOG_WARNING
  954.     gSMTPLogChkFlag "LogSend", LOG_SEND
  955.     gSMTPLogChkFlag "LogRecv", LOG_RECV
  956.     gSMTPLogChkFlag "LogSummary", LOG_SUMMARY
  957.     gSMTPLogChkFlag "LogNormal", LOG_NORMAL
  958.     gSMTPLogChkFlag "LogDebug", LOG_DEBUG
  959.  
  960.     giIndex = 0
  961.     giLogSize = 100
  962.  
  963.     Main!Ini.Entry = "LogSize"
  964.     If Main!Ini.Value = "" Then
  965.         Main!Ini.Value = Str$(giLogSize)
  966.     Else
  967.         giLogSize = Val(Main!Ini.Value)
  968.     End If
  969.     
  970.     gSMTPLog LOG_NORMAL, "Log size set to" & Str$(giLogSize) & " lines."
  971.  
  972.     CRLF = Chr$(13) + Chr$(10)
  973.     
  974.     ' *DIRTY* But it has to be done to get the
  975.     ' local host's name. Replace this with your
  976.     ' form and control array.
  977.     gLocalHostName = Main!NetClient(0).HostName
  978.  
  979.     cNetServer.LocalService = "smtp"
  980.     If cNetServer.LocalPort = 0 Then
  981.         cNetServer.LocalPort = 25
  982.     End If
  983. '    cNetServer.LocalPort = 2015
  984.    
  985.     cNetServer.QueueSize = 5
  986.     cNetServer.Listen = True
  987.  
  988.     Exit Sub
  989.  
  990. InitError:
  991.     gSMTPError Erl, Err, Error$
  992.     Resume Next
  993.  
  994. End Sub
  995.  
  996. Sub gSMTPTimeOut (Index As Integer)
  997.  
  998. End Sub
  999.  
  1000. Private Function HEX2Date (h$) As Double
  1001. Dim seconds#
  1002.  
  1003.     ' Stolen from a VB magazine (which I can't remember the name of)
  1004.     seconds# = Val("&H" + h$)
  1005.     If Len(h$) Then
  1006.         If seconds# > 0 Then
  1007.             HEX2Date = (seconds# - 1) / 86400 + 1
  1008.         Else
  1009.             HEX2Date = (seconds# + 4294967295#) / 86400 + 1
  1010.         End If
  1011.     End If
  1012.  
  1013. End Function
  1014.  
  1015.